package com.ht.WeNas.rest_controller;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ht.WeNas.data.dto.FileObj;
import com.ht.WeNas.data.entity.FbInfo;
import com.ht.WeNas.data.vo.Res;
import com.ht.WeNas.data.vo.ResPage;
import com.ht.WeNas.data.vo.fb.CreateFbForm;
import com.ht.WeNas.service.FileBucketService;
import com.ht.WeNas.utils.MyPathUtil;
import com.ht.WeNas.utils.RequestFileUtil;
import com.ht.WeNas.utils.VirtualPageUtil;
import com.ht.WeNas.utils.exceptions.MessageException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;

import java.nio.file.Path;
import java.util.Collection;
import java.util.List;

@Tag(name = "文件容器控制器")
@RestController
@RequestMapping("/api/fb")
public class FileBucketController {
    private final FileBucketService fileBucketService;

    public FileBucketController(FileBucketService fileBucketService) {
        this.fileBucketService = fileBucketService;
    }

    @Operation(summary = "分页获取文件容器信息")
    @GetMapping("/info")
    public Res<Page<FbInfo>> pageFileBucketInfo(
            @Parameter(description = "页码") @RequestParam(value = "current") Long current,
            @Parameter(description = "分页大小") @RequestParam(value = "size") Long size,
            @Parameter(description = "查询参数") @RequestParam(value = "search", required = false, defaultValue = "") String search,
            @Parameter(description = "容器类型") @RequestParam(value = "type", required = false, defaultValue = "") String type
    ) {
        Page<FbInfo> page = fileBucketService.pageFbInfo(size, current, search, type);
        return Res.ok(page);
    }
    
    @Operation(summary = "根据id获取文件容器信息")
    @GetMapping("/info/{id}")
    public Res<FbInfo> getFileBucketInfoById(@PathVariable("id") String id) {
        if (!NumberUtil.isLong(id)) {
            throw new MessageException("参数错误，id不是数字");
        }
        FbInfo info = fileBucketService.getFbInfoById(Long.valueOf(id));
        return Res.ok(info);
    }
    
    @Operation(summary = "创建文件容器")
    @PostMapping("/create")
    public Res<Void> createFileBucket(@RequestBody CreateFbForm form) {
        fileBucketService.createFileBucket(form.getName(), form.getRootPath());
        return Res.ok();
    }

    @Operation(summary = "删除文件容器")
    @DeleteMapping("/{id}")
    public Res<Void> deleteFileBucket(@PathVariable("id") long id) {
        fileBucketService.deleteById(id);
        return Res.ok();
    }

    @Operation(summary = "分页获取容器文件")
    @GetMapping("/page/{id}")
    public Res<ResPage<String>> pageFileById(
            @Parameter(description = "容器id") @PathVariable("id") String id,
            @Parameter(description = "分页大小") @RequestParam(value = "current") String current,
            @Parameter(description = "页码") @RequestParam(value = "size") String size,
            @Parameter(description = "限定文件类型前缀") @RequestParam(value = "contentTypePrefix", required = false) String contentTypePrefix
    ) {
        if (NumberUtil.isNumber(id) && NumberUtil.isNumber(current) && NumberUtil.isNumber(size)) {
            ResPage<Path> page = fileBucketService.pageFileById(
                    Long.parseLong(id),
                    Integer.parseInt(current), 
                    Integer.parseInt(size), 
                    contentTypePrefix
            );
            ResPage<String> resPage = ResPage.map(page, MyPathUtil::toPathString);
            return Res.ok(resPage);
        }
        throw new MessageException("参数错误");
    }

    @Operation(summary = "分页获取容器文件夹")
    @GetMapping("/page/dir/{id}")
    public Res<ResPage<String>> pageDirById(
            @Parameter(description = "容器id") @PathVariable("id") long id,
            @Parameter(description = "分页大小") @RequestParam("current") int current,
            @Parameter(description = "页码") @RequestParam("size") int size
    ) {
        ResPage<Path> page = fileBucketService.pageDirById(id, current, size);
        return Res.ok(ResPage.map(page, MyPathUtil::toPathString));
    }
    
    @Operation(summary = "分页获取容器文件夹内的文件")
    @GetMapping("/page/fileInDir/{id}")
    public Res<ResPage<String>> pageFileInDirById(
            @Parameter(description = "容器id") @PathVariable("id") long id,
            @Parameter(description = "容器内文件夹路径") @RequestParam("dirPath") String dirPath,
            @Parameter(description = "分页大小") @RequestParam("current") int current,
            @Parameter(description = "页码") @RequestParam("size") int size,
            @Parameter(description = "限定文件类型前缀") @RequestParam(value = "contentTypePrefix", required = false) String contentTypePrefix
    ) {
        ResPage<Path> page = fileBucketService.pageFileInDirById(id, dirPath, current, size, contentTypePrefix);
        return Res.ok(ResPage.map(page, MyPathUtil::toPathString));
    }

    @Operation(summary = "分页获取容器文件夹内的虚拟分页索引（第一个文件名）")
    @GetMapping("/page/vp/index/{id}")
    public Res<ResPage<String>> pageVirtualPageIndex(
            @Parameter(description = "容器id") @PathVariable("id") long id,
            @Parameter(description = "容器内文件夹路径") @RequestParam("dirPath") String dirPath,
            @Parameter(description = "分页大小") @RequestParam("current") int current,
            @Parameter(description = "页码") @RequestParam("size") int size,

            @Parameter(description = "最小打断长度")
            @RequestParam(value = "minBreakSize", required = false, defaultValue = "3")
            int minBreakSize,

            @Parameter(description = "指定的文件类型前缀")
            @RequestParam(value = "contentTypePrefix", required = false)
            String contentTypePrefix
    ) {
        List<Path> list = fileBucketService.listFileInDirById(id, dirPath);
        if (StrUtil.isNotBlank(contentTypePrefix)) {
            list = list.stream().filter(path -> MyPathUtil.contentTypeFilter(path, contentTypePrefix)).toList();
        }
        List<ResPage<String>> virtualPage = VirtualPageUtil.pathToVirtualPage(
                list,
                new int[]{1},
                new int[]{1},
                minBreakSize);
        List<String> res = virtualPage
                .stream()
                .map(ResPage::getRecords)
                .flatMap(Collection::stream)
                .toList();
        return Res.ok(ResPage.fromList(current, size, res));
    }

    @Operation(summary = "通过虚拟分页索引获取文件容器中指定文件夹里索引的虚拟分页")
    @GetMapping("/page/vp/{id}")
    public Res<ResPage<String>> pageVirtualPage(
            @Parameter(description = "容器id") @PathVariable("id") long id,
            @Parameter(description = "容器内文件夹路径") @RequestParam("dirPath") String dirPath,
            @Parameter(description = "虚拟分页索引（第一个文件路径）") @RequestParam("index") String index,
            @Parameter(description = "分页大小") @RequestParam("current") int current,
            @Parameter(description = "页码") @RequestParam("size") int size,

            @Parameter(description = "最小打断长度")
            @RequestParam(value = "minBreakSize", required = false, defaultValue = "3")
            int minBreakSize,

            @Parameter(description = "指定的文件类型前缀")
            @RequestParam(value = "contentTypePrefix", required = false)
            String contentTypePrefix
    ) {
        List<Path> list = fileBucketService.listFileInDirById(id, dirPath);
        if (StrUtil.isNotBlank(contentTypePrefix)) {
            list = list.stream().filter(path -> MyPathUtil.contentTypeFilter(path, contentTypePrefix)).toList();
        }
        List<ResPage<String>> virtualPage = VirtualPageUtil.pathToVirtualPage(
                list,
                new int[]{1},
                new int[]{Integer.MAX_VALUE},
                minBreakSize);
        for (ResPage<String> page : virtualPage) {
            List<String> records = page.getRecords();
            if (records == null || records.isEmpty()) {
                continue;
            }
            if (records.get(0).equals(index)) {
                return Res.ok(ResPage.fromList(current, size, records));
            }
        }
        return Res.ok(new ResPage<>(current, size, 0, List.of()));
    }

    @Operation(summary = "分页获取容器文件夹内的虚拟分页")
    @GetMapping("/page/virtualPage/{id}")
    public Res<ResPage<ResPage<String>>> pageVirtualPageInDir(
            @Parameter(description = "容器id") @PathVariable("id") long id,
            @Parameter(description = "容器内文件夹路径") @RequestParam("dirPath") String dirPath,
            @Parameter(description = "分页大小") @RequestParam("current") int current,
            @Parameter(description = "页码") @RequestParam("size") int size,
            
            @Parameter(description = "虚拟页码（数字以/分割）")
            @RequestParam(value = "virtualCurrent", required = false, defaultValue = "1") 
            String virtualCurrent,
            
            @Parameter(description = "虚拟分页大小（数字以/分割）") 
            @RequestParam(value = "virtualSize", required = false, defaultValue = "20") 
            String virtualSize,
            
            @Parameter(description = "最小打断长度")
            @RequestParam(value = "minBreakSize", required = false, defaultValue = "3")
            int minBreakSize,

            @Parameter(description = "指定的文件类型前缀")
            @RequestParam(value = "contentTypePrefix", required = false)
            String contentTypePrefix
    ) {
        List<Path> list = fileBucketService.listFileInDirById(id, dirPath);
        if (StrUtil.isNotBlank(contentTypePrefix)) {
            list = list.stream().filter(path -> MyPathUtil.contentTypeFilter(path, contentTypePrefix)).toList();
        }
        List<ResPage<String>> virtualPage = VirtualPageUtil.pathToVirtualPage(
                list,
                StrUtil.splitToInt(virtualCurrent, '/'),
                StrUtil.splitToInt(virtualSize, '/'),
                minBreakSize);
        ResPage<ResPage<String>> res = ResPage.fromList(current, size, virtualPage);
        return Res.ok(res);
    }
    
    @Operation(summary = "更新文件容器")
    @PostMapping("/update")
    public Res<Void> updateFileBucketInfo(@RequestBody FbInfo fbInfo) {
        fileBucketService.updateFileBucketInfo(fbInfo);
        return Res.ok();
    }
    
    @Operation(summary = "获取容器内随机文件路径")
    @GetMapping("/random/path/{id}")
    public Res<String> getRandomFilePath(
            @Parameter(description = "容器id") @PathVariable("id") long id,
            
            @Parameter(description = "容器内文件夹路径") 
            @RequestParam(value = "dirPath", required = false)
            String dirPath,
            
            @Parameter(description = "指定的文件类型前缀")
            @RequestParam(value = "contentTypePrefix", required = false)
            String contentTypePrefix
    ) {
        List<Path> list;
        if (StrUtil.isBlank(dirPath)) {
            list = fileBucketService.loadFiles(id);
        } else {
            list = fileBucketService.listFileInDirById(id, dirPath);
        }
        if (StrUtil.isNotBlank(contentTypePrefix)) {
            list = list.stream().filter(path -> MyPathUtil.contentTypeFilter(path, contentTypePrefix)).toList();
        }
        Path path = list.get(RandomUtil.randomInt(0, list.size()));
        return Res.ok(MyPathUtil.toPathString(path));
    }

    @Operation(summary = "获取容器内随机文件")
    @GetMapping("/random/{id}")
    public void getRandomFile(
            @Parameter(description = "容器id") @PathVariable("id") long id,

            @Parameter(description = "容器内文件夹路径")
            @RequestParam(value = "dirPath", required = false)
            String dirPath,

            @Parameter(description = "指定的文件类型前缀")
            @RequestParam(value = "contentTypePrefix", required = false)
            String contentTypePrefix,
            
            HttpServletResponse response
    ) {
        Res<String> res = this.getRandomFilePath(id, dirPath, contentTypePrefix);
        String path = res.getData();
        FileObj fileObj = FileObj.getInstance(Path.of(path));
        if (fileObj == null) {
            throw new MessageException("文件访问失败");
        }
        RequestFileUtil.returnFile(fileObj, response);
    }
}
